package com.qth.valet.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.qth.valet.common.enums.PropertyEnum;
import com.qth.valet.common.exception.PaymentException;
import com.qth.valet.common.utils.*;
import com.qth.valet.entity.po.*;
import com.qth.valet.entity.property.Commission;
import com.qth.valet.entity.req.PayReq;
import com.qth.valet.entity.vo.WxPayment;
import com.qth.valet.entity.vo.WxPaymentResponse;
import com.qth.valet.payment.WxPaymentConfig;
import com.qth.valet.service.*;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.notification.RSAPublicKeyNotificationConfig;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 小程序支付服务实现类
 *
 * @author Skyline
 */
@Slf4j
@Service
public class WechatPaymentServiceImpl implements WechatPaymentService {

    private final WxPaymentConfig wxPaymentConfig;
    private final Config config;

    private final JsapiService jsapiService;
    private final RefundService refundService;
    private final NotificationParser notificationParser;

    private static final String PAY_CHANNEL = "pc";
    private static final String COUPON_USER_ID = "cu";
    private static final String COUPON_AMOUNT = "cm";

    @Resource
    private OrdersService ordersService;
    @Resource
    private PaymentService paymentService;
    @Resource
    private DriversService driversService;
    @Resource
    private OrderHistoryService orderHistoryService;
    @Resource
    private CouponUserService couponUserService;
    @Resource
    private DriverBalanceDetailService driverBalanceDetailService;

    @Resource
    private PropertyService propertyService;

    @Resource
    private NoticeDriverService noticeDriverService;

    public WechatPaymentServiceImpl(WxPaymentConfig wxPaymentConfig, Config config) {
        this.wxPaymentConfig = wxPaymentConfig;
        this.config = config;
        // 初始化服务
        this.jsapiService = new JsapiService.Builder().config(config).build();
        this.refundService = new RefundService.Builder().config(config).build();

        // 初始化通知解析器（公钥模式使用RSAPublicKeyNotificationConfig）
        NotificationConfig notificationConfig = new RSAPublicKeyNotificationConfig.Builder()
                .publicKeyFromPath(wxPaymentConfig.getWechatPayPublicKeyPath())
                .publicKeyId(wxPaymentConfig.getPublicKeyId())
                .apiV3Key(wxPaymentConfig.getApiV3Key())
                .build();
        this.notificationParser = new NotificationParser(notificationConfig);
    }

    @Override
    public ResponseResult<WxPaymentResponse> createPayment(WxPayment payment, PayReq payReq, Coupon coupon) {
        try {
            log.info("创建微信支付订单，参数payment：{}，payReq：{}，coupon：{}", payment, payReq, coupon);

            // 生成商户订单号
            String outTradeNo = generateOutTradeNo(payment.getOrderId());
            //String outTradeNo = payment.getOrderId();

            // 构建统一下单请求
            PrepayRequest request = new PrepayRequest();
            request.setAppid(wxPaymentConfig.getAppId());
            request.setMchid(wxPaymentConfig.getMchId());
            request.setDescription(payment.getDescription());
            request.setOutTradeNo(outTradeNo);
            request.setNotifyUrl(wxPaymentConfig.getNotifyUrl());
            request.setAttach(setAttach(payReq, coupon));

            // 设置金额信息
            Amount amount = new Amount();
            amount.setTotal(payment.getAmount().multiply(new BigDecimal("100")).intValue()); // 转换为分
            amount.setCurrency("CNY");
            request.setAmount(amount);

            // 设置支付者信息
            Payer payer = new Payer();
            payer.setOpenid(payment.getOpenid());
            request.setPayer(payer);

            // 调用微信支付统一下单接口
            PrepayResponse response = jsapiService.prepay(request);
            String prepayId = response.getPrepayId();

            // 构建小程序支付参数
            WxPaymentResponse responseDTO = buildMiniProgramPayParams(prepayId);

            log.info("微信支付订单创建成功，订单号：{}，prepayId：{}", outTradeNo, prepayId);
            responseDTO.setOutTradeNo(outTradeNo);
            return ResponseResult.success(responseDTO);

        } catch (Exception e) {
            log.error("创建微信支付订单失败", e);
            return ResponseResult.fail("创建支付订单失败：" + e.getMessage());
        }
    }

    private String setAttach(PayReq payReq, Coupon coupon){
        JSONObject jsonObject = new JSONObject();
        jsonObject.putOnce(PAY_CHANNEL, payReq.getPayChannel());
        jsonObject.putOnce(COUPON_USER_ID, payReq.getCouponUserId());
        if(coupon != null){
            jsonObject.putOnce(COUPON_AMOUNT, coupon.getAmount());
        }

        return jsonObject.toString();
    }

    @Override
    public ResponseResult<Object> queryPayment(String orderId) {
        try {
            log.info("查询微信支付订单状态，参数：{}", orderId);

            QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
            request.setMchid(wxPaymentConfig.getMchId());
            request.setOutTradeNo(orderId);

            // 调用微信支付查询接口
            Transaction transaction = jsapiService.queryOrderByOutTradeNo(request);

            // 构建返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("trade_state", transaction.getTradeState().name());
            result.put("out_trade_no", transaction.getOutTradeNo());
            result.put("transaction_id", transaction.getTransactionId());
            result.put("trade_state_desc", transaction.getTradeStateDesc());
            result.put("success_time", transaction.getSuccessTime());

            if (transaction.getAmount() != null) {
                result.put("total", transaction.getAmount().getTotal());
                result.put("payer_total", transaction.getAmount().getPayerTotal());
                result.put("currency", transaction.getAmount().getCurrency());
                result.put("payer_currency", transaction.getAmount().getPayerCurrency());
            }

            log.info("查询微信支付订单状态成功：{}", result);
            return ResponseResult.success(result);

        } catch (Exception e) {
            log.error("查询微信支付订单状态失败", e);
            return ResponseResult.fail("查询支付状态失败：" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String handlePaymentNotify(HttpServletRequest request) {
        try {
            log.info("处理微信支付回调通知");

            // 构建请求参数
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(request.getHeader("Wechatpay-Serial"))
                    .nonce(request.getHeader("Wechatpay-Nonce"))
                    .signature(request.getHeader("Wechatpay-Signature"))
                    .timestamp(request.getHeader("Wechatpay-Timestamp"))
                    .body(getRequestBody(request))
                    .build();

            // 验证签名并解析通知数据
            Transaction transaction = notificationParser.parse(requestParam, Transaction.class);

            log.info("微信支付回调数据解析成功：订单号={}, 交易状态={}",
                    transaction.getOutTradeNo(), transaction.getTradeState());

            // 处理支付成功逻辑
            if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
                this.handlePaymentSuccess(transaction);
            }

            log.info("微信支付回调处理成功");
            return "SUCCESS";

        } catch (Exception e) {
            log.error("处理微信支付回调失败", e);
            // 抛出异常，否则不能回滚
            throw new RuntimeException(e);
        }
    }

    @Override
    public ResponseResult<Object> closePayment(String outTradeNo) {
        try {
            log.info("关闭微信支付订单，订单号：{}", outTradeNo);

            CloseOrderRequest request = new CloseOrderRequest();
            request.setMchid(wxPaymentConfig.getMchId());
            request.setOutTradeNo(outTradeNo);

            // 调用微信支付关闭订单接口
            jsapiService.closeOrder(request);

            log.info("微信支付订单关闭成功，订单号：{}", outTradeNo);
            return ResponseResult.success("订单关闭成功");

        } catch (Exception e) {
            log.error("关闭微信支付订单失败，订单号：{}", outTradeNo, e);
            return ResponseResult.fail("关闭支付订单失败：" + e.getMessage());
        }
    }

    @Override
    public ResponseResult<Object> refundPayment(String outTradeNo, Integer refundAmount, String refundReason) {
        try {
            log.info("申请退款，订单号：{}，退款金额：{}，退款原因：{}", outTradeNo, refundAmount, refundReason);

            // 生成退款单号
            String outRefundNo = "refund_" + System.currentTimeMillis();

            // 构建退款请求
            CreateRequest request = new CreateRequest();
            request.setOutTradeNo(outTradeNo);
            request.setOutRefundNo(outRefundNo);
            request.setReason(refundReason);

            // 设置退款金额
            com.wechat.pay.java.service.refund.model.AmountReq amountReq = new com.wechat.pay.java.service.refund.model.AmountReq();
            amountReq.setRefund(Long.valueOf(refundAmount));
            amountReq.setTotal(Long.valueOf(refundAmount)); // 这里应该是原订单金额，暂时使用退款金额
            amountReq.setCurrency("CNY");
            request.setAmount(amountReq);

            // 调用微信支付退款接口
            Refund refund = refundService.create(request);

            // 构建返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("out_trade_no", refund.getOutTradeNo());
            result.put("out_refund_no", refund.getOutRefundNo());
            result.put("refund_id", refund.getRefundId());
            result.put("status", refund.getStatus().name());
            result.put("create_time", refund.getCreateTime());

            if (refund.getAmount() != null) {
                result.put("refund_fee", refund.getAmount().getRefund());
                result.put("total_fee", refund.getAmount().getTotal());
            }

            log.info("申请退款成功，退款单号：{}", outRefundNo);
            return ResponseResult.success(result);

        } catch (Exception e) {
            log.error("申请退款失败", e);
            return ResponseResult.fail("申请退款失败：" + e.getMessage());
        }
    }

    /**
     * 生成商户订单号
     */
    private String generateOutTradeNo(String orderId) {
        ordersService.updatePayVersion(orderId);
        Orders order = ordersService.getById(orderId);
        Integer version = order.getPayVersion();

        return orderId+"_" + version;
    }

    /**
     * 构建小程序支付参数
     */
    private WxPaymentResponse buildMiniProgramPayParams(String prepayId) {
        WxPaymentResponse responseDTO = new WxPaymentResponse();
        responseDTO.setAppId(wxPaymentConfig.getAppId());
        responseDTO.setTimeStamp(String.valueOf(Instant.now().getEpochSecond()));
        responseDTO.setNonceStr(generateNonceStr());
        responseDTO.setPackageValue("prepay_id=" + prepayId);
        responseDTO.setSignType("RSA");
        responseDTO.setPrepayId(prepayId);

        // 构建签名字符串
        String signStr = responseDTO.getAppId() + "\n" +
                responseDTO.getTimeStamp() + "\n" +
                responseDTO.getNonceStr() + "\n" +
                responseDTO.getPackageValue() + "\n";

        // 使用配置中的私钥生成签名
        try {
            String paySign = config.createSigner().sign(signStr).getSign();
            responseDTO.setPaySign(paySign);
        } catch (Exception e) {
            log.error("生成支付签名失败", e);
            throw new RuntimeException("生成支付签名失败", e);
        }

        return responseDTO;
    }

    /**
     * 生成随机字符串
     */
    private String generateNonceStr() {
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 32; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }

    /**
     * 读取请求体
     */
    private String getRequestBody(HttpServletRequest request) throws IOException {
        StringBuilder sb = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        }
        return sb.toString();
    }

    /**
     * 处理支付成功逻辑
     */
    private void handlePaymentSuccess(Transaction transaction) {
        // 订单ID
        String outTradeNo = transaction.getOutTradeNo();
        String orderId = outTradeNo.split("_")[0];
        // 查询订单
        Orders dbOrder = ordersService.getById(orderId);
        if (dbOrder == null) {
            throw new PaymentException();
        }
        // 1. 更新订单
        LambdaUpdateWrapper<Orders> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Orders::getId, orderId)
                .set(Orders::getStatus, StatusConstants.ORDER_FINISH)
                .set(Orders::getFinishTime, new Date());
        boolean update = ordersService.update(updateWrapper);
        if (!update) throw new PaymentException();

        // 解析attach
        String attach = transaction.getAttach();
        JSONObject jsonObject = JSONUtil.parseObj(attach);
        Integer payChannel = jsonObject.get(PAY_CHANNEL, Integer.class);
        Long couponUserId = jsonObject.get(COUPON_USER_ID, Long.class);
        Long couponAmount = jsonObject.get(COUPON_AMOUNT, Long.class);

        // 2. 创建流水
        Payment payment = this.buildPayment(payChannel, couponUserId, dbOrder, couponAmount, transaction.getTransactionId());
        paymentService.save(payment);
        // 3. 更新优惠券状态
        if (couponUserId != null) {
            couponUserService.update(new LambdaUpdateWrapper<CouponUser>()
                    .eq(CouponUser::getId, couponUserId)
                    .set(CouponUser::getOrderId, orderId)
                    .set(CouponUser::getIsUsed, true)
                    .set(CouponUser::getUseTime, new Date()));
        }

        // 查询司机信息
        Driver driver = driversService.getById(dbOrder.getDriverId());
        if (driver == null) {
            log.error("司机不存在，ID：{}", dbOrder.getDriverId());
            throw new RuntimeException("司机不存在");
        }

        // 当前司机 增加余额 + 插入明细 + 通知
        // 更新司机余额
        driversService.updateDriverBalancePlus(driver.getId(), dbOrder.getPrice());
        log.info("更新司机账户余额，司机ID：{}，增加{}", driver.getId(), dbOrder.getPrice());
        // 插入明细
        DriverBalanceDetail driverBalanceDetail = new DriverBalanceDetail();
        driverBalanceDetail.setDriverId(driver.getId());                               // 当前司机ID
        driverBalanceDetail.setOrderId(orderId);
        driverBalanceDetail.setBalanceType(StatusConstants.BALANCE_TYPE_DRIVE_INCOME); // 代驾收入
        driverBalanceDetail.setAmount(dbOrder.getPrice());
        // 余额已修改，直接查
        driverBalanceDetail.setRemainingBalance(driversService.getById(driver.getId()).getAccountBalance());
        driverBalanceDetailService.save(driverBalanceDetail);

        // 通知
        noticeDriverService.save(new NoticeDriver()
                .setDriverId(driver.getId())
                .setType(StatusConstants.NOTICE_DRIVE_INCOME)
                .setContent("代驾收入" + AmountUtil.formatAmount(dbOrder.getPrice()) + "元")
        );
        // 将司机的通知读取状态改为未读
        driversService.updateNoticeReadStatus(driver.getId(), false);

        // 保存一条历史记录
        orderHistoryService.saveOrderHistory(orderId, dbOrder.getUserId(), null, "用户支付成功");
        // 例如：更新订单状态、发送通知等
        log.info("处理支付成功逻辑，订单号：{}，微信交易号：{}", transaction.getOutTradeNo(), transaction.getTransactionId());
    }

    private Payment buildPayment(Integer payChannel, Long couponUserId, Orders dbOrder, Long couponAmount, String transactionId) {
        Payment payment = new Payment();
        payment.setUserId(dbOrder.getUserId());
        payment.setOrderId(dbOrder.getId());
        payment.setType(0);   //支付状态
        payment.setTotalAmount(dbOrder.getPrice());
        payment.setPayChannel(payChannel);
        payment.setPayTime(new Date());
        payment.setTransactionId(transactionId);
        if (couponAmount != null) {
            // 有优惠券
            payment.setCouponUserId(couponUserId);
            payment.setCouponAmount(couponAmount);
            payment.setPayAmount(dbOrder.getPrice() - couponAmount);
        } else {
            // 没优惠券
            payment.setPayAmount(dbOrder.getPrice());
        }
        if (payment.getPayAmount() < 0L) {
            payment.setPayAmount(0L);
        }
        return payment;
    }

}
